<template>
  <n-form
    ref="formRef"
    v-bind="formProps"
    :model="formValue"
    @keydown="formKeydownHandler"
  >
    <n-grid
      v-if="!collapsable || (collapsable && !collapsed)"
      :cols="formItemGridCols"
      :x-gap="formItemGap"
    >
      <n-form-item-gi
        v-for="{
          path,
          label,
          span,
          component = 'NInput',
          componentModelValue = 'value',
          componentProps,
          description,
          descriptionPlacement = 'right',
          descriptionTriggerIcon = 'InformationIcon',
          ...restProps
        } in schemas"
        :key="path"
        :label="label"
        :path="path"
        :span="span ?? formItemGridCols"
        v-bind="restProps"
      >
        <template #label>
          <z-icon-tip
            :content="label"
            :tooltip="description"
            :placement="descriptionPlacement"
            :icon="descriptionTriggerIcon"
          />
        </template>

        <component
          :is="component"
          v-bind="componentProps"
          v-model:[componentModelValue]="formValue[path]"
        ></component>
        <!-- TODO: path support 'a.b.c' -->
      </n-form-item-gi>

      <n-gi v-if="hasFormAction" :span="formActionSpan">
        <slot name="formAction" :formActions="formMethods">
          <!-- TODO: collapse while items takes more than 2 rows -->
          <n-space :style="{ justifyContent: formActionJustifyContent }">
            <slot name="formActionBefore" :formActions="formMethods"></slot>
            <n-button v-if="resetActionText" @click="handleReset">
              {{ resetActionText }}
            </n-button>
            <n-button
              v-if="submitActionText"
              type="primary"
              @click="handleSubmit"
            >
              {{ submitActionText }}
            </n-button>
            <slot name="formActionAfter" :formActions="formMethods"></slot>

            <n-button v-if="collapsable" @click="fold"> 折叠 </n-button>
          </n-space>
        </slot>
      </n-gi>
    </n-grid>

    <div v-if="collapsable && collapsed">
      <n-space :style="{ justifyContent: formActionJustifyContent }">
        <n-button @click="unfold"> 展开 </n-button>
      </n-space>
    </div>
  </n-form>
</template>

<script setup lang="ts">
import { FormProps, FormInst } from "naive-ui";
import { Justify } from "naive-ui/es/space/src/Space";
import { ref } from "vue";
import { useFormCore } from "./hooks/core";
import { ZFormItemSchema } from "./typings";
import { useBoolean } from "@/hooks/bool";

export interface ZFormProps {
  schemas: ZFormItemSchema[];
  formProps?: FormProps;
  // default 24, how many cols for grid
  formItemGridCols?: number;
  formItemGap?: number;
  hasFormAction?: boolean;
  formActionSpan?: number;
  formActionJustifyContent?: Justify;
  submitActionText?: string;
  resetActionText?: string;
  // can be folded
  collapsable?: boolean;
  // submit when `Enter` keydown
  submitOnEnter?: boolean;
}
const props = withDefaults(defineProps<ZFormProps>(), {
  hasFormAction: true,
  formActionSpan: 24,
  formActionJustifyContent: "end",
  formItemGridCols: 24,
  formItemGap: 0,
  submitActionText: "提交",
  resetActionText: "重置",
  collapsable: false,
  submitOnEnter: true,
});

/* collapse */
const [collapsed, fold, unfold] = useBoolean();

const formRef = ref<FormInst | null>(null);
const { formValue, formMethods } = useFormCore(props, formRef);

async function formKeydownHandler(e: KeyboardEvent) {
  if (e.key === "Enter") {
    props.submitOnEnter && handleSubmit();
    emit("enterKeydown");
  }
}

/* emits */
const emit = defineEmits<{
  (event: "submit", values: any): void;
  (event: "reset"): void;
  (event: "enterKeydown"): void;
}>();
async function handleSubmit() {
  const values = await formMethods.validate();
  emit("submit", values);
}

async function handleReset() {
  formMethods.resetFields();
  emit("reset");
}

defineExpose(formMethods);
</script>

<script lang="ts">
/* components registry */
import { defineComponent } from "vue";
import components from "./components/index";
export default defineComponent({
  components,
});
</script>
